17 October review

New Data review : “C:\20251016scholars.Rda”

Data Prep Work

Clear data

rm(list=ls()) #start clean

Load Functions

library(readxl)
library(selenider)
library(rvest)
library(tidyverse)
library(netstat)
library(pingr)
library(jsonlite)
library(stringr)
library(openalexR)
library(readxl)

packages <- c("tidyverse", "scholar", "openalexR", "rvest", "jsonlite")
packages <- c("devtools", "igraph")

fpackage.check <- function(packages) {
    lapply(packages, FUN = function(x) {
        if (!require(x, character.only = TRUE)) {
            install.packages(x, dependencies = TRUE)
            library(x, character.only = TRUE)
        }
    })
}

fsave <- function(x, file = NULL, location = "./data/processed/") {
    ifelse(!dir.exists("data"), dir.create("data"), FALSE)
    ifelse(!dir.exists("data/processed"), dir.create("data/processed"), FALSE)
    if (is.null(file))
        file = deparse(substitute(x))
    datename <- substr(gsub("[:-]", "", Sys.time()), 1, 8)
    totalname <- paste(location, file, "_", datename, ".rda", sep = "")
    save(x, file = totalname)  #need to fix if file is reloaded as input name, not as x. 
}

fload <- function(filename) {
    load(filename)
    get(ls()[ls() != "filename"])
}

fshowdf <- function(x, ...) {
    knitr::kable(x, digits = 2, "html", ...) %>%
        kableExtra::kable_styling(bootstrap_options = c("striped", "hover")) %>%
        kableExtra::scroll_box(width = "100%", height = "300px")
}

Access large data file of professors, set of egos for research

Load Dataset - new dataset from Jos

scholars <- fload("C:/Github/labjournal/20251016scholars.Rda") 

New fcolnet function

Define Network Data Helper Function

fcolnet = function(data = scholars, university = c("RU", 'UU'), discipline = "Sociologie", waves = list(c(2015,
    2018), c(2019, 2023), c(2024, 2025)), type = c("first")) {

    university = paste0('(', paste0(university, collapse='|' ), ')')
    discipline = paste0('(', paste0(discipline, collapse='|' ), ')')

    # step 1
    demographics = data$demographics
    sample = which(
        (str_detect(demographics$universiteit.22, university)
            | str_detect(demographics$universiteit.24, university)
            | str_detect(demographics$universiteit.25, university)
        ) & (
            str_detect(demographics$discipline.22, discipline)
            | str_detect(demographics$discipline.24, discipline)
            | str_detect(demographics$discipline.25, discipline)
        ) |> replace_na(FALSE))

    demographics_soc = demographics[sample, ] |> drop_na(id)

    # step 2
    ids = demographics_soc$id |> unique()

    scholars_sel = list() 
    for (id_ in ids){
        scholars_sel[[id_]] = bind_rows(scholars$works) |>
            filter(author_id == id_)
    }
    scholars_sel = bind_rows(scholars$works) 
    

    nwaves = length(waves)
    nets = array(0, dim = c(nwaves, length(ids), length(ids)), dimnames = list(wave = 1:nwaves, ids,
        ids))
    dimnames(nets)

    # step 3
    df_works = tibble(
            works_id = scholars_sel$id, 
            works_author = scholars_sel$authorships, 
            works_year = scholars_sel$publication_year
        )

    df_works = df_works[!duplicated(df_works), ]

    # step 4
    if (type == "first") {
        for (j in 1:length(waves)) {
            df_works_w = df_works[df_works$works_year >= waves[[j]][1] & df_works$works_year <= waves[[j]][2],
                ]
            for (i in 1:nrow(df_works_w)) {
                ego = df_works_w$works_author[i][[1]]$id[1]
                alters = df_works_w$works_author[i][[1]]$id[-1]
                if (sum(ids %in% ego) > 0 & sum(ids %in% alters) > 0) {
                  nets[j, which(ids %in% ego), which(ids %in% alters)] = 1
                }
            }
        }
    }

    if (type == "last") {
        for (j in 1:length(waves)) {
            df_works_w = df_works[df_works$works_year >= waves[[j]][1] & df_works$works_year <= waves[[j]][2],
                ]
            for (i in 1:nrow(df_works_w)) {
                ego = rev(df_works_w$works_author[i][[1]]$id[1])
                alters = rev(df_works_w$works_author[i][[1]]$id[-1])
                if (sum(ids %in% ego) > 0 & sum(ids %in% alters) > 0) {
                  nets[j, which(ids %in% ego), which(ids %in% alters)] = 1
                }
            }
        }
    }
    if (type == "all") {
        for (j in 1:length(waves)) {
            df_works_w = df_works[df_works$works_year >= waves[[j]][1] & df_works$works_year <= waves[[j]][2],
                ]
            for (i in 1:nrow(df_works_w)) {
                egos = df_works_w$works_author[i][[1]]$id
                if (sum(ids %in% egos) > 0) {
                  nets[j, which(ids %in% egos), which(ids %in% egos)] = 1
                }
            }
            diag(nets[j,,]) = 0
        }
    }

    output = list()
    output$data = demographics_soc
    output$nets = nets
    return(output)
}

load Rsiena packages

packages = c(
    "RSiena", "tidyverse",
    'dplyr', 'stringr' # these packages were added to make the code run
)
fpackage.check(packages)

Getting Data

Application

Load Data

# from Jos code - Radboud and Utrecht
test1 = fcolnet(scholars, university = c('RU', 'UU')) 
df_ego1 = bind_rows(test1$data)

# Radboud only (where I want to start)
test = fcolnet(scholars, university = c("RU")) #only Radboud 
df_ego = bind_rows(test$data)

Wrangle Data

wave1 = test$nets[1,,]
wave2 = test$nets[2,,]
wave3 = test$nets[3,,]

nets = array(
    data = c(wave1, wave2, wave3),
    dim = c(dim(wave2), 2)
)

net = sienaDependent(nets)

Isolate Gender variable (binary)

# Example from recoding function
#df_ego = df_ego |>
#    mutate(
#        funcs = case_when(
#            functie.22 == "Full Professor" ~ 1,
#            functie.24 == "Full Professor" ~ 1,
#            functie.25 == "Full Professor" ~ 1,
#            .default = 0
#        )
#    )

# Recoding for gender
df_ego = df_ego |>
    mutate(
        female = case_when(
            gender == "female" ~ 1,
            .default = 0
        )
    )
female = coCovar(df_ego$female)

Visualizing RU Waves

# make adjacency matrix with first wave of data
test_wave1ru <- igraph::graph_from_adjacency_matrix(
  test$nets[1,,], #for this example I take the first wave of data. (thus I select the array of networks and take the first matrix)
  mode = c("directed"),
  weighted = NULL,
  diag = FALSE,
  add.colnames = NULL,
  add.rownames = NULL
)

#plot to see if it worked 
plot(test_wave1ru,
  vertex.color = ifelse(df_ego$female == 1, "red", "blue"),
  vertex.label = NA,
  edge.width = 0.2,
  edge.arrow.size =0.2)

dim(test_wave1ru) #check it works 
sum(is.na(test_wave1ru)) #check it is complete -- if 0 missing values

Visualizing just radboud (both depts) wave 2

test_wave2ru <- igraph::graph_from_adjacency_matrix(
  test$nets[2,,], #for this example I take the first wave of data. (thus I select the array of networks and take the first matrix)
  mode = c("directed"),
  weighted = NULL,
  diag = FALSE,
  add.colnames = NULL,
  add.rownames = NULL
)

#plot to see if it worked 
plot(test_wave2ru,
  vertex.color = ifelse(df_ego$female == 1, "red", "blue"),
  vertex.label = NA,
  edge.width = 0.2,
  edge.arrow.size =0.2)
test_wave3ru <- igraph::graph_from_adjacency_matrix(
  test$nets[3,,], #for this example I take the first wave of data. (thus I select the array of networks and take the first matrix)
  mode = c("directed"),
  weighted = NULL,
  diag = FALSE,
  add.colnames = NULL,
  add.rownames = NULL
)

#plot to see if it worked 
plot(test_wave3ru,
  vertex.color = ifelse(df_ego$female == 1, "red", "blue"),
  vertex.label = NA,
  edge.width = 0.2,
  edge.arrow.size =0.2)

Add in collaborators + their gender?

Descriptive Statistics

NOW - LOOK AT DESCRIPTIVE STATISTICS FOR RU ONLY


#SIZE
# number of nodes for RU professors
vcount(test_wave1ru) #returns 160
vcount(test_wave2ru) #returns 160
vcount(test_wave3ru) #returns 160 

#SIZE - for reference
# number of nodes for all professors
#vcount(test_w1) #returns 674
#vcount(test_w2) #returns 674


#EDGES
# number of edges for RU professors
ecount(test_wave1ru) #returns 49
ecount(test_wave2ru) #returns 138
ecount(test_wave3ru) #returns 75


#DEGREE
# looking at clustering and spread
igraph::degree(test_wave1ru)
igraph::degree(test_wave2ru)
igraph::degree(test_wave3ru)


hist(table(degree(test_wave1ru)), xlab='indegree', main= 'Histogram of indegree') 
# every number is the degree level of each actor -- and see it is heavily skewed to the left
# Wave 1: see frequency of 7 for indegree 0:50, frequency of 0 for indegree 50:100, frequency 1 for indegree 100:150

hist(table(degree(test_wave2ru)), xlab='indegree', main= 'Histogram of indegree') # every number is the degree level of each actor -- and see it is heavily left skewed too  
# Wave 2: see frequency of 10 for indegree 0:20, frequency of 2 for indegree 20:40, 0 for 40:60, 1 for 60:80

hist(table(degree(test_wave3ru)), xlab='indegree', main= 'Histogram of indegree') # every number is the degree level of each actor -- and see it is heavily left skewed too  
# Wave 3: see frequency of 4 for indegree 0:20, frequency of 2 for indegree 20:40, 0 for 40:80, 1 for 80:100


#TRANSITIVITY -- all of these return "NAN" -- check?
# directed: be aware that directed graphs are considered as undirected. CHECK IF TEST_W1 AND 2 ARE DIRECTED OR UNDIRECTED.
## FLAG - ERROR WITH THIS - NOT ABLE TO REALLY USE/VIEW RESULTS
igraph::transitivity(test_wave1ru, type = c("localundirected"), isolates = c("NaN", "zero")) #differences pop out less 
igraph::transitivity(test_wave2ru, type = c("localundirected"), isolates = c("NaN", "zero")) #differences pop out less 
igraph::transitivity(test_wave3ru, type = c("localundirected"), isolates = c("NaN", "zero")) #differences pop out less 


#BETWEENNESS
# directed: be aware that directed graphs are considered as undirected. CHECK IF TEST_W1 AND 2 ARE DIRECTED OR UNDIRECTED.
igraph::transitivity(test_wave1ru, type = c("localundirected"), isolates = c("NaN", "zero"))
igraph::transitivity(test_wave2ru, type = c("localundirected"), isolates = c("NaN", "zero"))
igraph::transitivity(test_wave3ru, type = c("localundirected"), isolates = c("NaN", "zero"))

Next, moving from local to global transitivity

  • Look at triads for more global transitivity.
  • Note: Global = number of observed over possible - can identify all transitive triads and all possible triads
  • Reviewing dyads - then triads. Since it is undirected, it is less difficult to calculate.
  • Now, looking at triad census vs triad allegation
# plot: igraph - XX <- make_graph(y) <- test$nets[1,,] ??
# adj mat: XX <- as_adj_matrix((plot), type = "both", sparse = FALSE) -- adj mat = test_w1 =  test$nets[1,,]


igraph::dyad.census(test_wave1ru) #with plot -- works 
  # Returns: 7 mut, 35 asym, 12678 null 
igraph::dyad.census(test_wave2ru) #with plot -- works
  # Returns: 13 mut, 112 asym, 12575 null 
igraph::dyad.census(test_wave3ru) #with plot -- works
  # Returns: 3 mut, 69 asym, 12648 null 


igraph::triad.census(test_wave1ru) #with plot -- works
  # Returns:  [1] 663364   5405   1076     11     24     11     15      6      2      0      3      3      0      0      0      0

igraph::triad.census(test_wave2ru) #with plot -- works
  # Returns:   [1] 650587  16986   1963     37    189     58     56     10     14      0      1      8      4      4      2      1

igraph::triad.census(test_wave3ru) #with plot -- works
  # Returns:   [1] 658675  10658    462     28     68     13      8      2      5      0      0      0      0      0      1      0
library(sna)

# Wave 1
sna::triad.census(test$nets[1,,]) #with adj matrix of test_wave1ru -- triad.census of (test_w1) doesn't work. 
unloadNamespace("sna")  #detach this package again to avoid interference with other igraph functions 
  # Returns:         003  012  102   021D 021U 021C 111D 111U 030T 030C 201 120D 120U 120C 210 300
           # [1,] 663364  5405 1076  11   24   11   15    6    2    0   3    3    0    0   0   0
  # Same as igraph triad census! 


igraph::transitivity(test_wave1ru, type = "global") #with plot
  # Returns: [1] 0.1764706
sna::gtrans(test$nets[1,,]) #triad census a different way, but this is with plot - need with adj mat:
  # Returns: [1] 0.173913
  ## Prev Code: sna::gtrans(test$nets[1,,]) #with adj matrix

triad_w1ru <- data.frame(sna::triad.census(test$nets[1,,])) #save as df, #with adj matrix

transitivity_w1 <- (3 * triad_w1ru$X300)/(triad_w1ru$X201 + 3 * triad_w1ru$X300) #X300 is variable for transitive triad (the fully closed triad) - we multiply by 3 because there are 3 possible transitive triads

transitivity_w1
  # Returns 0 (?)




# Wave 2
sna::triad.census(test$nets[2,,])
unloadNamespace("sna")  #I will detach this package again

triad_w2ru <- data.frame(sna::triad.census(test$nets[2,,])) #save as df


igraph::transitivity(test_wave2ru, type = "global")
  # Returns: [1] 0.22
sna::gtrans(test$nets[2,,]) #triad census a different way 
  # Returns: [1] 0.2842105

transitivity_w2 <- (3 * triad_w2ru$X300)/(triad_w2ru$X201 + 3 * triad_w2ru$X300) #X300 is variable for transitive triad (the fully closed triad)
# we multiply by 3 because there are 3 possible transitive triads
transitivity_w2
  # Returns: [1] 0.75



# Wave 3
sna::triad.census(test$nets[3,,])
   # Returns: 003    012   102   021D  021U  021C  111D  111U  030T  030C 201  120D  120U 120C  210  300
   #    [1,]  658675 10658 462   28    68    13     8     2     5    0    0    0     0     0     1   0

unloadNamespace("sna")  #I will detach this package again

triad_w3ru <- data.frame(sna::triad.census(test$nets[3,,])) #save as df


igraph::transitivity(test_wave3ru, type = "global")
  # Returns: [1] 0.1313869
sna::gtrans(test$nets[3,,]) #triad census a different way 
  # Returns: [1] 0.25

transitivity_w3 <- (3 * triad_w3ru$X300)/(triad_w3ru$X201 + 3 * triad_w3ru$X300) #X300 is variable for transitive triad (the fully closed triad)
# we multiply by 3 because there are 3 possible transitive triads
transitivity_w3
  # Returns: [1] NaN

NEED TO INCLUDE TRIADS - TRANSITIVITY OUTDEGREE AND RECIPROCITY ARE ALWAYS IN THERE, ALSO NEED OUTDEGREE ACTIVITY OR IN DEGREE POPULARITY. ALSO NEED SOMETHING FOR TRANSITIVITY - GWESP VARIABLE AND EFFECTS TO INCLUDE - MAKE SURE TO INCLUDE ONE OF THESE TOO.

Network visualisation: Let’s make size proportional to betweenness score

# changing V of Wave1
V(test_wave1ru)$size = betweenness(test_wave1ru, normalized = T, directed = FALSE) * 60 + 10  #after some trial and error


 ## multiplication - changing 60 changes the difference in size,, adding 10 makes the smallest visible
plot(test_wave1ru, mode = "undirected")
 ## stuck: need to remove ids


# igraph, want no overlap: igraph plotting no overlap -- a lot of layout functions -- want to hold printing device constant, and then reduce overlap...the idea is to push least central egos out 

set.seed(2345)
l <- layout_with_mds(test_wave1ru)  #https://igraph.org/r/doc/layout_with_mds.html
plot(test_wave1ru, layout = l)
# story in second plot: 5 clusters ? (around XX, XX, XX, XX, and XX) - and in-between (which wasn't as clear before)
 ## stuck: need to remove ids


#NOTE: REF LAB 4 TO MODIFY THE THE SIZING/APPEARANCE OF THE NETWORK VISUALS

IF NEEDED LATER:

Now, try to loop in gender

Make simple adj matrix for gender

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KMTcgT2N0b2JlciByZXZpZXcgDQoNCk5ldyBEYXRhIHJldmlldyA6ICJDOlxHaXRodWJcbGFiam91cm5hbFwyMDI1MTAxNnNjaG9sYXJzLlJkYSINCg0KIyBEYXRhIFByZXAgV29yayANCg0KIyMgQ2xlYXIgZGF0YQ0KYGBge3J9DQpybShsaXN0PWxzKCkpICNzdGFydCBjbGVhbg0KYGBgDQoNCiMjIExvYWQgRnVuY3Rpb25zDQpgYGB7cn0NCmxpYnJhcnkocmVhZHhsKQ0KbGlicmFyeShzZWxlbmlkZXIpDQpsaWJyYXJ5KHJ2ZXN0KQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KG5ldHN0YXQpDQpsaWJyYXJ5KHBpbmdyKQ0KbGlicmFyeShqc29ubGl0ZSkNCmxpYnJhcnkoc3RyaW5ncikNCmxpYnJhcnkob3BlbmFsZXhSKQ0KbGlicmFyeShyZWFkeGwpDQoNCnBhY2thZ2VzIDwtIGMoInRpZHl2ZXJzZSIsICJzY2hvbGFyIiwgIm9wZW5hbGV4UiIsICJydmVzdCIsICJqc29ubGl0ZSIpDQpwYWNrYWdlcyA8LSBjKCJkZXZ0b29scyIsICJpZ3JhcGgiKQ0KDQpmcGFja2FnZS5jaGVjayA8LSBmdW5jdGlvbihwYWNrYWdlcykgew0KICAgIGxhcHBseShwYWNrYWdlcywgRlVOID0gZnVuY3Rpb24oeCkgew0KICAgICAgICBpZiAoIXJlcXVpcmUoeCwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKSkgew0KICAgICAgICAgICAgaW5zdGFsbC5wYWNrYWdlcyh4LCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KICAgICAgICAgICAgbGlicmFyeSh4LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpDQogICAgICAgIH0NCiAgICB9KQ0KfQ0KDQpmc2F2ZSA8LSBmdW5jdGlvbih4LCBmaWxlID0gTlVMTCwgbG9jYXRpb24gPSAiLi9kYXRhL3Byb2Nlc3NlZC8iKSB7DQogICAgaWZlbHNlKCFkaXIuZXhpc3RzKCJkYXRhIiksIGRpci5jcmVhdGUoImRhdGEiKSwgRkFMU0UpDQogICAgaWZlbHNlKCFkaXIuZXhpc3RzKCJkYXRhL3Byb2Nlc3NlZCIpLCBkaXIuY3JlYXRlKCJkYXRhL3Byb2Nlc3NlZCIpLCBGQUxTRSkNCiAgICBpZiAoaXMubnVsbChmaWxlKSkNCiAgICAgICAgZmlsZSA9IGRlcGFyc2Uoc3Vic3RpdHV0ZSh4KSkNCiAgICBkYXRlbmFtZSA8LSBzdWJzdHIoZ3N1YigiWzotXSIsICIiLCBTeXMudGltZSgpKSwgMSwgOCkNCiAgICB0b3RhbG5hbWUgPC0gcGFzdGUobG9jYXRpb24sIGZpbGUsICJfIiwgZGF0ZW5hbWUsICIucmRhIiwgc2VwID0gIiIpDQogICAgc2F2ZSh4LCBmaWxlID0gdG90YWxuYW1lKSAgI25lZWQgdG8gZml4IGlmIGZpbGUgaXMgcmVsb2FkZWQgYXMgaW5wdXQgbmFtZSwgbm90IGFzIHguIA0KfQ0KDQpmbG9hZCA8LSBmdW5jdGlvbihmaWxlbmFtZSkgew0KICAgIGxvYWQoZmlsZW5hbWUpDQogICAgZ2V0KGxzKClbbHMoKSAhPSAiZmlsZW5hbWUiXSkNCn0NCg0KZnNob3dkZiA8LSBmdW5jdGlvbih4LCAuLi4pIHsNCiAgICBrbml0cjo6a2FibGUoeCwgZGlnaXRzID0gMiwgImh0bWwiLCAuLi4pICU+JQ0KICAgICAgICBrYWJsZUV4dHJhOjprYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKSAlPiUNCiAgICAgICAga2FibGVFeHRyYTo6c2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIiwgaGVpZ2h0ID0gIjMwMHB4IikNCn0NCg0KYGBgDQoNCg0KIyMgQWNjZXNzIGxhcmdlIGRhdGEgZmlsZSBvZiBwcm9mZXNzb3JzLCBzZXQgb2YgZWdvcyBmb3IgcmVzZWFyY2gNCg0KIyMjIExvYWQgRGF0YXNldCAtIG5ldyBkYXRhc2V0IGZyb20gSm9zDQoNCmBgYHtyfQ0Kc2Nob2xhcnMgPC0gZmxvYWQoIkM6L0dpdGh1Yi9sYWJqb3VybmFsLzIwMjUxMDE2c2Nob2xhcnMuUmRhIikgDQpgYGANCg0KDQojIyBOZXcgZmNvbG5ldCBmdW5jdGlvbg0KDQojIyMgRGVmaW5lIE5ldHdvcmsgRGF0YSBIZWxwZXIgRnVuY3Rpb24NCmBgYHtyfQ0KZmNvbG5ldCA9IGZ1bmN0aW9uKGRhdGEgPSBzY2hvbGFycywgdW5pdmVyc2l0eSA9IGMoIlJVIiwgJ1VVJyksIGRpc2NpcGxpbmUgPSAiU29jaW9sb2dpZSIsIHdhdmVzID0gbGlzdChjKDIwMTUsDQogICAgMjAxOCksIGMoMjAxOSwgMjAyMyksIGMoMjAyNCwgMjAyNSkpLCB0eXBlID0gYygiZmlyc3QiKSkgew0KDQogICAgdW5pdmVyc2l0eSA9IHBhc3RlMCgnKCcsIHBhc3RlMCh1bml2ZXJzaXR5LCBjb2xsYXBzZT0nfCcgKSwgJyknKQ0KICAgIGRpc2NpcGxpbmUgPSBwYXN0ZTAoJygnLCBwYXN0ZTAoZGlzY2lwbGluZSwgY29sbGFwc2U9J3wnICksICcpJykNCg0KICAgICMgc3RlcCAxDQogICAgZGVtb2dyYXBoaWNzID0gZGF0YSRkZW1vZ3JhcGhpY3MNCiAgICBzYW1wbGUgPSB3aGljaCgNCiAgICAgICAgKHN0cl9kZXRlY3QoZGVtb2dyYXBoaWNzJHVuaXZlcnNpdGVpdC4yMiwgdW5pdmVyc2l0eSkNCiAgICAgICAgICAgIHwgc3RyX2RldGVjdChkZW1vZ3JhcGhpY3MkdW5pdmVyc2l0ZWl0LjI0LCB1bml2ZXJzaXR5KQ0KICAgICAgICAgICAgfCBzdHJfZGV0ZWN0KGRlbW9ncmFwaGljcyR1bml2ZXJzaXRlaXQuMjUsIHVuaXZlcnNpdHkpDQogICAgICAgICkgJiAoDQogICAgICAgICAgICBzdHJfZGV0ZWN0KGRlbW9ncmFwaGljcyRkaXNjaXBsaW5lLjIyLCBkaXNjaXBsaW5lKQ0KICAgICAgICAgICAgfCBzdHJfZGV0ZWN0KGRlbW9ncmFwaGljcyRkaXNjaXBsaW5lLjI0LCBkaXNjaXBsaW5lKQ0KICAgICAgICAgICAgfCBzdHJfZGV0ZWN0KGRlbW9ncmFwaGljcyRkaXNjaXBsaW5lLjI1LCBkaXNjaXBsaW5lKQ0KICAgICAgICApIHw+IHJlcGxhY2VfbmEoRkFMU0UpKQ0KDQogICAgZGVtb2dyYXBoaWNzX3NvYyA9IGRlbW9ncmFwaGljc1tzYW1wbGUsIF0gfD4gZHJvcF9uYShpZCkNCg0KICAgICMgc3RlcCAyDQogICAgaWRzID0gZGVtb2dyYXBoaWNzX3NvYyRpZCB8PiB1bmlxdWUoKQ0KDQoNCiAgICBzY2hvbGFyc19zZWwgPSBsaXN0KCkgDQogICAgZm9yIChpZF8gaW4gaWRzKXsNCiAgICAgICAgc2Nob2xhcnNfc2VsW1tpZF9dXSA9IGJpbmRfcm93cyhzY2hvbGFycyR3b3JrcykgfD4NCiAgICAgICAgICAgIGZpbHRlcihhdXRob3JfaWQgPT0gaWRfKQ0KICAgIH0NCiAgICBzY2hvbGFyc19zZWwgPSBiaW5kX3Jvd3Moc2Nob2xhcnMkd29ya3MpIA0KICAgIA0KDQogICAgbndhdmVzID0gbGVuZ3RoKHdhdmVzKQ0KICAgIG5ldHMgPSBhcnJheSgwLCBkaW0gPSBjKG53YXZlcywgbGVuZ3RoKGlkcyksIGxlbmd0aChpZHMpKSwgZGltbmFtZXMgPSBsaXN0KHdhdmUgPSAxOm53YXZlcywgaWRzLA0KICAgICAgICBpZHMpKQ0KICAgIGRpbW5hbWVzKG5ldHMpDQoNCiAgICAjIHN0ZXAgMw0KICAgIGRmX3dvcmtzID0gdGliYmxlKA0KICAgICAgICAgICAgd29ya3NfaWQgPSBzY2hvbGFyc19zZWwkaWQsIA0KICAgICAgICAgICAgd29ya3NfYXV0aG9yID0gc2Nob2xhcnNfc2VsJGF1dGhvcnNoaXBzLCANCiAgICAgICAgICAgIHdvcmtzX3llYXIgPSBzY2hvbGFyc19zZWwkcHVibGljYXRpb25feWVhcg0KICAgICAgICApDQoNCg0KICAgIGRmX3dvcmtzID0gZGZfd29ya3NbIWR1cGxpY2F0ZWQoZGZfd29ya3MpLCBdDQoNCiAgICAjIHN0ZXAgNA0KICAgIGlmICh0eXBlID09ICJmaXJzdCIpIHsNCiAgICAgICAgZm9yIChqIGluIDE6bGVuZ3RoKHdhdmVzKSkgew0KICAgICAgICAgICAgZGZfd29ya3NfdyA9IGRmX3dvcmtzW2RmX3dvcmtzJHdvcmtzX3llYXIgPj0gd2F2ZXNbW2pdXVsxXSAmIGRmX3dvcmtzJHdvcmtzX3llYXIgPD0gd2F2ZXNbW2pdXVsyXSwNCiAgICAgICAgICAgICAgICBdDQogICAgICAgICAgICBmb3IgKGkgaW4gMTpucm93KGRmX3dvcmtzX3cpKSB7DQogICAgICAgICAgICAgICAgZWdvID0gZGZfd29ya3NfdyR3b3Jrc19hdXRob3JbaV1bWzFdXSRpZFsxXQ0KICAgICAgICAgICAgICAgIGFsdGVycyA9IGRmX3dvcmtzX3ckd29ya3NfYXV0aG9yW2ldW1sxXV0kaWRbLTFdDQogICAgICAgICAgICAgICAgaWYgKHN1bShpZHMgJWluJSBlZ28pID4gMCAmIHN1bShpZHMgJWluJSBhbHRlcnMpID4gMCkgew0KICAgICAgICAgICAgICAgICAgbmV0c1tqLCB3aGljaChpZHMgJWluJSBlZ28pLCB3aGljaChpZHMgJWluJSBhbHRlcnMpXSA9IDENCiAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9DQogICAgICAgIH0NCiAgICB9DQoNCiAgICBpZiAodHlwZSA9PSAibGFzdCIpIHsNCiAgICAgICAgZm9yIChqIGluIDE6bGVuZ3RoKHdhdmVzKSkgew0KICAgICAgICAgICAgZGZfd29ya3NfdyA9IGRmX3dvcmtzW2RmX3dvcmtzJHdvcmtzX3llYXIgPj0gd2F2ZXNbW2pdXVsxXSAmIGRmX3dvcmtzJHdvcmtzX3llYXIgPD0gd2F2ZXNbW2pdXVsyXSwNCiAgICAgICAgICAgICAgICBdDQogICAgICAgICAgICBmb3IgKGkgaW4gMTpucm93KGRmX3dvcmtzX3cpKSB7DQogICAgICAgICAgICAgICAgZWdvID0gcmV2KGRmX3dvcmtzX3ckd29ya3NfYXV0aG9yW2ldW1sxXV0kaWRbMV0pDQogICAgICAgICAgICAgICAgYWx0ZXJzID0gcmV2KGRmX3dvcmtzX3ckd29ya3NfYXV0aG9yW2ldW1sxXV0kaWRbLTFdKQ0KICAgICAgICAgICAgICAgIGlmIChzdW0oaWRzICVpbiUgZWdvKSA+IDAgJiBzdW0oaWRzICVpbiUgYWx0ZXJzKSA+IDApIHsNCiAgICAgICAgICAgICAgICAgIG5ldHNbaiwgd2hpY2goaWRzICVpbiUgZWdvKSwgd2hpY2goaWRzICVpbiUgYWx0ZXJzKV0gPSAxDQogICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfQ0KICAgICAgICB9DQogICAgfQ0KICAgIGlmICh0eXBlID09ICJhbGwiKSB7DQogICAgICAgIGZvciAoaiBpbiAxOmxlbmd0aCh3YXZlcykpIHsNCiAgICAgICAgICAgIGRmX3dvcmtzX3cgPSBkZl93b3Jrc1tkZl93b3JrcyR3b3Jrc195ZWFyID49IHdhdmVzW1tqXV1bMV0gJiBkZl93b3JrcyR3b3Jrc195ZWFyIDw9IHdhdmVzW1tqXV1bMl0sDQogICAgICAgICAgICAgICAgXQ0KICAgICAgICAgICAgZm9yIChpIGluIDE6bnJvdyhkZl93b3Jrc193KSkgew0KICAgICAgICAgICAgICAgIGVnb3MgPSBkZl93b3Jrc193JHdvcmtzX2F1dGhvcltpXVtbMV1dJGlkDQogICAgICAgICAgICAgICAgaWYgKHN1bShpZHMgJWluJSBlZ29zKSA+IDApIHsNCiAgICAgICAgICAgICAgICAgIG5ldHNbaiwgd2hpY2goaWRzICVpbiUgZWdvcyksIHdoaWNoKGlkcyAlaW4lIGVnb3MpXSA9IDENCiAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9DQogICAgICAgICAgICBkaWFnKG5ldHNbaiwsXSkgPSAwDQogICAgICAgIH0NCiAgICB9DQoNCiAgICBvdXRwdXQgPSBsaXN0KCkNCiAgICBvdXRwdXQkZGF0YSA9IGRlbW9ncmFwaGljc19zb2MNCiAgICBvdXRwdXQkbmV0cyA9IG5ldHMNCiAgICByZXR1cm4ob3V0cHV0KQ0KfQ0KYGBgDQoNCiMjIyBsb2FkIFJzaWVuYSBwYWNrYWdlcw0KYGBge3J9DQpwYWNrYWdlcyA9IGMoDQogICAgIlJTaWVuYSIsICJ0aWR5dmVyc2UiLA0KICAgICdkcGx5cicsICdzdHJpbmdyJyAjIHRoZXNlIHBhY2thZ2VzIHdlcmUgYWRkZWQgdG8gbWFrZSB0aGUgY29kZSBydW4NCikNCmZwYWNrYWdlLmNoZWNrKHBhY2thZ2VzKQ0KDQpgYGANCg0KDQojIEdldHRpbmcgRGF0YQ0KDQojIyBBcHBsaWNhdGlvbg0KDQojIyMgTG9hZCBEYXRhDQpgYGB7cn0NCiMgZnJvbSBKb3MgY29kZSAtIFJhZGJvdWQgYW5kIFV0cmVjaHQNCnRlc3QxID0gZmNvbG5ldChzY2hvbGFycywgdW5pdmVyc2l0eSA9IGMoJ1JVJywgJ1VVJykpIA0KZGZfZWdvMSA9IGJpbmRfcm93cyh0ZXN0MSRkYXRhKQ0KDQojIFJhZGJvdWQgb25seSAod2hlcmUgSSB3YW50IHRvIHN0YXJ0KQ0KdGVzdCA9IGZjb2xuZXQoc2Nob2xhcnMsIHVuaXZlcnNpdHkgPSBjKCJSVSIpKSAjb25seSBSYWRib3VkIA0KZGZfZWdvID0gYmluZF9yb3dzKHRlc3QkZGF0YSkNCmBgYA0KDQojIyMgV3JhbmdsZSBEYXRhDQpgYGB7cn0NCndhdmUxID0gdGVzdCRuZXRzWzEsLF0NCndhdmUyID0gdGVzdCRuZXRzWzIsLF0NCndhdmUzID0gdGVzdCRuZXRzWzMsLF0NCg0KbmV0cyA9IGFycmF5KA0KICAgIGRhdGEgPSBjKHdhdmUxLCB3YXZlMiwgd2F2ZTMpLA0KICAgIGRpbSA9IGMoZGltKHdhdmUyKSwgMikNCikNCg0KbmV0ID0gc2llbmFEZXBlbmRlbnQobmV0cykNCmBgYA0KDQoNCiMjIElzb2xhdGUgR2VuZGVyIHZhcmlhYmxlIChiaW5hcnkpDQoNCmBgYHtyfQ0KIyBFeGFtcGxlIGZyb20gcmVjb2RpbmcgZnVuY3Rpb24NCiNkZl9lZ28gPSBkZl9lZ28gfD4NCiMgICAgbXV0YXRlKA0KIyAgICAgICAgZnVuY3MgPSBjYXNlX3doZW4oDQojICAgICAgICAgICAgZnVuY3RpZS4yMiA9PSAiRnVsbCBQcm9mZXNzb3IiIH4gMSwNCiMgICAgICAgICAgICBmdW5jdGllLjI0ID09ICJGdWxsIFByb2Zlc3NvciIgfiAxLA0KIyAgICAgICAgICAgIGZ1bmN0aWUuMjUgPT0gIkZ1bGwgUHJvZmVzc29yIiB+IDEsDQojICAgICAgICAgICAgLmRlZmF1bHQgPSAwDQojICAgICAgICApDQojICAgICkNCg0KIyBSZWNvZGluZyBmb3IgZ2VuZGVyDQpkZl9lZ28gPSBkZl9lZ28gfD4NCiAgICBtdXRhdGUoDQogICAgICAgIGZlbWFsZSA9IGNhc2Vfd2hlbigNCiAgICAgICAgICAgIGdlbmRlciA9PSAiZmVtYWxlIiB+IDEsDQogICAgICAgICAgICAuZGVmYXVsdCA9IDANCiAgICAgICAgKQ0KICAgICkNCmZlbWFsZSA9IGNvQ292YXIoZGZfZWdvJGZlbWFsZSkNCmBgYA0KDQoNCg0KIyMgVmlzdWFsaXppbmcgUlUgV2F2ZXMNCg0KYGBge3J9DQojIG1ha2UgYWRqYWNlbmN5IG1hdHJpeCB3aXRoIGZpcnN0IHdhdmUgb2YgZGF0YQ0KdGVzdF93YXZlMXJ1IDwtIGlncmFwaDo6Z3JhcGhfZnJvbV9hZGphY2VuY3lfbWF0cml4KA0KICB0ZXN0JG5ldHNbMSwsXSwgI2ZvciB0aGlzIGV4YW1wbGUgSSB0YWtlIHRoZSBmaXJzdCB3YXZlIG9mIGRhdGEuICh0aHVzIEkgc2VsZWN0IHRoZSBhcnJheSBvZiBuZXR3b3JrcyBhbmQgdGFrZSB0aGUgZmlyc3QgbWF0cml4KQ0KICBtb2RlID0gYygiZGlyZWN0ZWQiKSwNCiAgd2VpZ2h0ZWQgPSBOVUxMLA0KICBkaWFnID0gRkFMU0UsDQogIGFkZC5jb2xuYW1lcyA9IE5VTEwsDQogIGFkZC5yb3duYW1lcyA9IE5VTEwNCikNCg0KI3Bsb3QgdG8gc2VlIGlmIGl0IHdvcmtlZCANCnBsb3QodGVzdF93YXZlMXJ1LA0KICB2ZXJ0ZXguY29sb3IgPSBpZmVsc2UoZGZfZWdvJGZlbWFsZSA9PSAxLCAicmVkIiwgImJsdWUiKSwNCiAgdmVydGV4LmxhYmVsID0gTkEsDQogIGVkZ2Uud2lkdGggPSAwLjIsDQogIGVkZ2UuYXJyb3cuc2l6ZSA9MC4yKQ0KDQpkaW0odGVzdF93YXZlMXJ1KSAjY2hlY2sgaXQgd29ya3MgDQpzdW0oaXMubmEodGVzdF93YXZlMXJ1KSkgI2NoZWNrIGl0IGlzIGNvbXBsZXRlIC0tIGlmIDAgbWlzc2luZyB2YWx1ZXMNCg0KYGBgDQoNCg0KIyMgVmlzdWFsaXppbmcganVzdCByYWRib3VkIChib3RoIGRlcHRzKSB3YXZlIDINCmBgYHtyfQ0KdGVzdF93YXZlMnJ1IDwtIGlncmFwaDo6Z3JhcGhfZnJvbV9hZGphY2VuY3lfbWF0cml4KA0KICB0ZXN0JG5ldHNbMiwsXSwgI2ZvciB0aGlzIGV4YW1wbGUgSSB0YWtlIHRoZSBmaXJzdCB3YXZlIG9mIGRhdGEuICh0aHVzIEkgc2VsZWN0IHRoZSBhcnJheSBvZiBuZXR3b3JrcyBhbmQgdGFrZSB0aGUgZmlyc3QgbWF0cml4KQ0KICBtb2RlID0gYygiZGlyZWN0ZWQiKSwNCiAgd2VpZ2h0ZWQgPSBOVUxMLA0KICBkaWFnID0gRkFMU0UsDQogIGFkZC5jb2xuYW1lcyA9IE5VTEwsDQogIGFkZC5yb3duYW1lcyA9IE5VTEwNCikNCg0KI3Bsb3QgdG8gc2VlIGlmIGl0IHdvcmtlZCANCnBsb3QodGVzdF93YXZlMnJ1LA0KICB2ZXJ0ZXguY29sb3IgPSBpZmVsc2UoZGZfZWdvJGZlbWFsZSA9PSAxLCAicmVkIiwgImJsdWUiKSwNCiAgdmVydGV4LmxhYmVsID0gTkEsDQogIGVkZ2Uud2lkdGggPSAwLjIsDQogIGVkZ2UuYXJyb3cuc2l6ZSA9MC4yKQ0KYGBgDQoNCg0KYGBge3J9DQp0ZXN0X3dhdmUzcnUgPC0gaWdyYXBoOjpncmFwaF9mcm9tX2FkamFjZW5jeV9tYXRyaXgoDQogIHRlc3QkbmV0c1szLCxdLCAjZm9yIHRoaXMgZXhhbXBsZSBJIHRha2UgdGhlIGZpcnN0IHdhdmUgb2YgZGF0YS4gKHRodXMgSSBzZWxlY3QgdGhlIGFycmF5IG9mIG5ldHdvcmtzIGFuZCB0YWtlIHRoZSBmaXJzdCBtYXRyaXgpDQogIG1vZGUgPSBjKCJkaXJlY3RlZCIpLA0KICB3ZWlnaHRlZCA9IE5VTEwsDQogIGRpYWcgPSBGQUxTRSwNCiAgYWRkLmNvbG5hbWVzID0gTlVMTCwNCiAgYWRkLnJvd25hbWVzID0gTlVMTA0KKQ0KDQojcGxvdCB0byBzZWUgaWYgaXQgd29ya2VkIA0KcGxvdCh0ZXN0X3dhdmUzcnUsDQogIHZlcnRleC5jb2xvciA9IGlmZWxzZShkZl9lZ28kZmVtYWxlID09IDEsICJyZWQiLCAiYmx1ZSIpLA0KICB2ZXJ0ZXgubGFiZWwgPSBOQSwNCiAgZWRnZS53aWR0aCA9IDAuMiwNCiAgZWRnZS5hcnJvdy5zaXplID0wLjIpDQpgYGANCg0KDQojIEFkZCBpbiBjb2xsYWJvcmF0b3JzICsgdGhlaXIgZ2VuZGVyPw0KYGBge3J9DQpgYGANCg0KDQoNCmBgYHtyfQ0KYGBgDQoNCg0KDQojIERlc2NyaXB0aXZlIFN0YXRpc3RpY3MNCiMjIE5PVyAtIExPT0sgQVQgREVTQ1JJUFRJVkUgU1RBVElTVElDUyBGT1IgUlUgT05MWSANCg0KYGBge3J9DQoNCiNTSVpFDQojIG51bWJlciBvZiBub2RlcyBmb3IgUlUgcHJvZmVzc29ycw0KdmNvdW50KHRlc3Rfd2F2ZTFydSkgI3JldHVybnMgMTYwDQp2Y291bnQodGVzdF93YXZlMnJ1KSAjcmV0dXJucyAxNjANCnZjb3VudCh0ZXN0X3dhdmUzcnUpICNyZXR1cm5zIDE2MCANCg0KI1NJWkUgLSBmb3IgcmVmZXJlbmNlDQojIG51bWJlciBvZiBub2RlcyBmb3IgYWxsIHByb2Zlc3NvcnMNCiN2Y291bnQodGVzdF93MSkgI3JldHVybnMgNjc0DQojdmNvdW50KHRlc3RfdzIpICNyZXR1cm5zIDY3NA0KDQoNCiNFREdFUw0KIyBudW1iZXIgb2YgZWRnZXMgZm9yIFJVIHByb2Zlc3NvcnMNCmVjb3VudCh0ZXN0X3dhdmUxcnUpICNyZXR1cm5zIDQ5DQplY291bnQodGVzdF93YXZlMnJ1KSAjcmV0dXJucyAxMzgNCmVjb3VudCh0ZXN0X3dhdmUzcnUpICNyZXR1cm5zIDc1DQoNCg0KI0RFR1JFRQ0KIyBsb29raW5nIGF0IGNsdXN0ZXJpbmcgYW5kIHNwcmVhZA0KaWdyYXBoOjpkZWdyZWUodGVzdF93YXZlMXJ1KQ0KaWdyYXBoOjpkZWdyZWUodGVzdF93YXZlMnJ1KQ0KaWdyYXBoOjpkZWdyZWUodGVzdF93YXZlM3J1KQ0KDQoNCmhpc3QodGFibGUoZGVncmVlKHRlc3Rfd2F2ZTFydSkpLCB4bGFiPSdpbmRlZ3JlZScsIG1haW49ICdIaXN0b2dyYW0gb2YgaW5kZWdyZWUnKSANCiMgZXZlcnkgbnVtYmVyIGlzIHRoZSBkZWdyZWUgbGV2ZWwgb2YgZWFjaCBhY3RvciAtLSBhbmQgc2VlIGl0IGlzIGhlYXZpbHkgc2tld2VkIHRvIHRoZSBsZWZ0DQojIFdhdmUgMTogc2VlIGZyZXF1ZW5jeSBvZiA3IGZvciBpbmRlZ3JlZSAwOjUwLCBmcmVxdWVuY3kgb2YgMCBmb3IgaW5kZWdyZWUgNTA6MTAwLCBmcmVxdWVuY3kgMSBmb3IgaW5kZWdyZWUgMTAwOjE1MA0KDQpoaXN0KHRhYmxlKGRlZ3JlZSh0ZXN0X3dhdmUycnUpKSwgeGxhYj0naW5kZWdyZWUnLCBtYWluPSAnSGlzdG9ncmFtIG9mIGluZGVncmVlJykgIyBldmVyeSBudW1iZXIgaXMgdGhlIGRlZ3JlZSBsZXZlbCBvZiBlYWNoIGFjdG9yIC0tIGFuZCBzZWUgaXQgaXMgaGVhdmlseSBsZWZ0IHNrZXdlZCB0b28gIA0KIyBXYXZlIDI6IHNlZSBmcmVxdWVuY3kgb2YgMTAgZm9yIGluZGVncmVlIDA6MjAsIGZyZXF1ZW5jeSBvZiAyIGZvciBpbmRlZ3JlZSAyMDo0MCwgMCBmb3IgNDA6NjAsIDEgZm9yIDYwOjgwDQoNCmhpc3QodGFibGUoZGVncmVlKHRlc3Rfd2F2ZTNydSkpLCB4bGFiPSdpbmRlZ3JlZScsIG1haW49ICdIaXN0b2dyYW0gb2YgaW5kZWdyZWUnKSAjIGV2ZXJ5IG51bWJlciBpcyB0aGUgZGVncmVlIGxldmVsIG9mIGVhY2ggYWN0b3IgLS0gYW5kIHNlZSBpdCBpcyBoZWF2aWx5IGxlZnQgc2tld2VkIHRvbyAgDQojIFdhdmUgMzogc2VlIGZyZXF1ZW5jeSBvZiA0IGZvciBpbmRlZ3JlZSAwOjIwLCBmcmVxdWVuY3kgb2YgMiBmb3IgaW5kZWdyZWUgMjA6NDAsIDAgZm9yIDQwOjgwLCAxIGZvciA4MDoxMDANCg0KDQojVFJBTlNJVElWSVRZIC0tIGFsbCBvZiB0aGVzZSByZXR1cm4gIk5BTiIgLS0gY2hlY2s/DQojIGRpcmVjdGVkOiBiZSBhd2FyZSB0aGF0IGRpcmVjdGVkIGdyYXBocyBhcmUgY29uc2lkZXJlZCBhcyB1bmRpcmVjdGVkLiBDSEVDSyBJRiBURVNUX1cxIEFORCAyIEFSRSBESVJFQ1RFRCBPUiBVTkRJUkVDVEVELg0KIyMgRkxBRyAtIEVSUk9SIFdJVEggVEhJUyAtIE5PVCBBQkxFIFRPIFJFQUxMWSBVU0UvVklFVyBSRVNVTFRTDQppZ3JhcGg6OnRyYW5zaXRpdml0eSh0ZXN0X3dhdmUxcnUsIHR5cGUgPSBjKCJsb2NhbHVuZGlyZWN0ZWQiKSwgaXNvbGF0ZXMgPSBjKCJOYU4iLCAiemVybyIpKSAjZGlmZmVyZW5jZXMgcG9wIG91dCBsZXNzIA0KaWdyYXBoOjp0cmFuc2l0aXZpdHkodGVzdF93YXZlMnJ1LCB0eXBlID0gYygibG9jYWx1bmRpcmVjdGVkIiksIGlzb2xhdGVzID0gYygiTmFOIiwgInplcm8iKSkgI2RpZmZlcmVuY2VzIHBvcCBvdXQgbGVzcyANCmlncmFwaDo6dHJhbnNpdGl2aXR5KHRlc3Rfd2F2ZTNydSwgdHlwZSA9IGMoImxvY2FsdW5kaXJlY3RlZCIpLCBpc29sYXRlcyA9IGMoIk5hTiIsICJ6ZXJvIikpICNkaWZmZXJlbmNlcyBwb3Agb3V0IGxlc3MgDQoNCg0KI0JFVFdFRU5ORVNTDQojIGRpcmVjdGVkOiBiZSBhd2FyZSB0aGF0IGRpcmVjdGVkIGdyYXBocyBhcmUgY29uc2lkZXJlZCBhcyB1bmRpcmVjdGVkLiBDSEVDSyBJRiBURVNUX1cxIEFORCAyIEFSRSBESVJFQ1RFRCBPUiBVTkRJUkVDVEVELg0KaWdyYXBoOjp0cmFuc2l0aXZpdHkodGVzdF93YXZlMXJ1LCB0eXBlID0gYygibG9jYWx1bmRpcmVjdGVkIiksIGlzb2xhdGVzID0gYygiTmFOIiwgInplcm8iKSkNCmlncmFwaDo6dHJhbnNpdGl2aXR5KHRlc3Rfd2F2ZTJydSwgdHlwZSA9IGMoImxvY2FsdW5kaXJlY3RlZCIpLCBpc29sYXRlcyA9IGMoIk5hTiIsICJ6ZXJvIikpDQppZ3JhcGg6OnRyYW5zaXRpdml0eSh0ZXN0X3dhdmUzcnUsIHR5cGUgPSBjKCJsb2NhbHVuZGlyZWN0ZWQiKSwgaXNvbGF0ZXMgPSBjKCJOYU4iLCAiemVybyIpKQ0KDQpgYGANCg0KDQoNCiMjIE5leHQsIG1vdmluZyBmcm9tIGxvY2FsIHRvIGdsb2JhbCB0cmFuc2l0aXZpdHkgDQotICAgTG9vayBhdCB0cmlhZHMgZm9yIG1vcmUgZ2xvYmFsIHRyYW5zaXRpdml0eS4gDQotICAgTm90ZTogR2xvYmFsID0gbnVtYmVyIG9mIG9ic2VydmVkIG92ZXIgcG9zc2libGUgLSBjYW4gaWRlbnRpZnkgYWxsIHRyYW5zaXRpdmUgdHJpYWRzIGFuZCBhbGwgcG9zc2libGUgdHJpYWRzIA0KLSAgIFJldmlld2luZyBkeWFkcyAtIHRoZW4gdHJpYWRzLiBTaW5jZSBpdCBpcyB1bmRpcmVjdGVkLCBpdCBpcyBsZXNzIGRpZmZpY3VsdCB0byBjYWxjdWxhdGUuIA0KLSAgIE5vdywgbG9va2luZyBhdCB0cmlhZCBjZW5zdXMgdnMgdHJpYWQgYWxsZWdhdGlvbg0KDQpgYGB7cn0NCiMgcGxvdDogaWdyYXBoIC0gWFggPC0gbWFrZV9ncmFwaCh5KSA8LSB0ZXN0JG5ldHNbMSwsXSA/Pw0KIyBhZGogbWF0OiBYWCA8LSBhc19hZGpfbWF0cml4KChwbG90KSwgdHlwZSA9ICJib3RoIiwgc3BhcnNlID0gRkFMU0UpIC0tIGFkaiBtYXQgPSB0ZXN0X3cxID0gIHRlc3QkbmV0c1sxLCxdDQoNCg0KaWdyYXBoOjpkeWFkLmNlbnN1cyh0ZXN0X3dhdmUxcnUpICN3aXRoIHBsb3QgLS0gd29ya3MgDQogICMgUmV0dXJuczogNyBtdXQsIDM1IGFzeW0sIDEyNjc4IG51bGwgDQppZ3JhcGg6OmR5YWQuY2Vuc3VzKHRlc3Rfd2F2ZTJydSkgI3dpdGggcGxvdCAtLSB3b3Jrcw0KICAjIFJldHVybnM6IDEzIG11dCwgMTEyIGFzeW0sIDEyNTc1IG51bGwgDQppZ3JhcGg6OmR5YWQuY2Vuc3VzKHRlc3Rfd2F2ZTNydSkgI3dpdGggcGxvdCAtLSB3b3Jrcw0KICAjIFJldHVybnM6IDMgbXV0LCA2OSBhc3ltLCAxMjY0OCBudWxsIA0KDQoNCmlncmFwaDo6dHJpYWQuY2Vuc3VzKHRlc3Rfd2F2ZTFydSkgI3dpdGggcGxvdCAtLSB3b3Jrcw0KICAjIFJldHVybnM6ICBbMV0gNjYzMzY0ICAgNTQwNSAgIDEwNzYgICAgIDExICAgICAyNCAgICAgMTEgICAgIDE1ICAgICAgNiAgICAgIDIgICAgICAwICAgICAgMyAgICAgIDMgICAgICAwICAgICAgMCAgICAgIDAgICAgICAwDQoNCmlncmFwaDo6dHJpYWQuY2Vuc3VzKHRlc3Rfd2F2ZTJydSkgI3dpdGggcGxvdCAtLSB3b3Jrcw0KICAjIFJldHVybnM6ICAgWzFdIDY1MDU4NyAgMTY5ODYgICAxOTYzICAgICAzNyAgICAxODkgICAgIDU4ICAgICA1NiAgICAgMTAgICAgIDE0ICAgICAgMCAgICAgIDEgICAgICA4ICAgICAgNCAgICAgIDQgICAgICAyICAgICAgMQ0KDQppZ3JhcGg6OnRyaWFkLmNlbnN1cyh0ZXN0X3dhdmUzcnUpICN3aXRoIHBsb3QgLS0gd29ya3MNCiAgIyBSZXR1cm5zOiAgIFsxXSA2NTg2NzUgIDEwNjU4ICAgIDQ2MiAgICAgMjggICAgIDY4ICAgICAxMyAgICAgIDggICAgICAyICAgICAgNSAgICAgIDAgICAgICAwICAgICAgMCAgICAgIDAgICAgICAwICAgICAgMSAgICAgIDANCg0KDQoNCmBgYA0KDQoNCg0KYGBge3J9DQpsaWJyYXJ5KHNuYSkNCg0KIyBXYXZlIDENCnNuYTo6dHJpYWQuY2Vuc3VzKHRlc3QkbmV0c1sxLCxdKSAjd2l0aCBhZGogbWF0cml4IG9mIHRlc3Rfd2F2ZTFydSAtLSB0cmlhZC5jZW5zdXMgb2YgKHRlc3RfdzEpIGRvZXNuJ3Qgd29yay4gDQp1bmxvYWROYW1lc3BhY2UoInNuYSIpICAjZGV0YWNoIHRoaXMgcGFja2FnZSBhZ2FpbiB0byBhdm9pZCBpbnRlcmZlcmVuY2Ugd2l0aCBvdGhlciBpZ3JhcGggZnVuY3Rpb25zIA0KICAjIFJldHVybnM6ICAgICAgICAgMDAzICAwMTIgIDEwMiAgIDAyMUQgMDIxVSAwMjFDIDExMUQgMTExVSAwMzBUIDAzMEMgMjAxIDEyMEQgMTIwVSAxMjBDIDIxMCAzMDANCiAgICAgICAgICAgIyBbMSxdIDY2MzM2NCAgNTQwNSAxMDc2ICAxMSAgIDI0ICAgMTEgICAxNSAgICA2ICAgIDIgICAgMCAgIDMgICAgMyAgICAwICAgIDAgICAwICAgMA0KICAjIFNhbWUgYXMgaWdyYXBoIHRyaWFkIGNlbnN1cyEgDQoNCg0KaWdyYXBoOjp0cmFuc2l0aXZpdHkodGVzdF93YXZlMXJ1LCB0eXBlID0gImdsb2JhbCIpICN3aXRoIHBsb3QNCiAgIyBSZXR1cm5zOiBbMV0gMC4xNzY0NzA2DQpzbmE6Omd0cmFucyh0ZXN0JG5ldHNbMSwsXSkgI3RyaWFkIGNlbnN1cyBhIGRpZmZlcmVudCB3YXksIGJ1dCB0aGlzIGlzIHdpdGggcGxvdCAtIG5lZWQgd2l0aCBhZGogbWF0Og0KICAjIFJldHVybnM6IFsxXSAwLjE3MzkxMw0KICAjIyBQcmV2IENvZGU6IHNuYTo6Z3RyYW5zKHRlc3QkbmV0c1sxLCxdKSAjd2l0aCBhZGogbWF0cml4DQoNCnRyaWFkX3cxcnUgPC0gZGF0YS5mcmFtZShzbmE6OnRyaWFkLmNlbnN1cyh0ZXN0JG5ldHNbMSwsXSkpICNzYXZlIGFzIGRmLCAjd2l0aCBhZGogbWF0cml4DQoNCnRyYW5zaXRpdml0eV93MSA8LSAoMyAqIHRyaWFkX3cxcnUkWDMwMCkvKHRyaWFkX3cxcnUkWDIwMSArIDMgKiB0cmlhZF93MXJ1JFgzMDApICNYMzAwIGlzIHZhcmlhYmxlIGZvciB0cmFuc2l0aXZlIHRyaWFkICh0aGUgZnVsbHkgY2xvc2VkIHRyaWFkKSAtIHdlIG11bHRpcGx5IGJ5IDMgYmVjYXVzZSB0aGVyZSBhcmUgMyBwb3NzaWJsZSB0cmFuc2l0aXZlIHRyaWFkcw0KDQp0cmFuc2l0aXZpdHlfdzENCiAgIyBSZXR1cm5zIDAgKD8pDQoNCg0KDQoNCiMgV2F2ZSAyDQpzbmE6OnRyaWFkLmNlbnN1cyh0ZXN0JG5ldHNbMiwsXSkNCnVubG9hZE5hbWVzcGFjZSgic25hIikgICNJIHdpbGwgZGV0YWNoIHRoaXMgcGFja2FnZSBhZ2Fpbg0KDQp0cmlhZF93MnJ1IDwtIGRhdGEuZnJhbWUoc25hOjp0cmlhZC5jZW5zdXModGVzdCRuZXRzWzIsLF0pKSAjc2F2ZSBhcyBkZg0KDQoNCmlncmFwaDo6dHJhbnNpdGl2aXR5KHRlc3Rfd2F2ZTJydSwgdHlwZSA9ICJnbG9iYWwiKQ0KICAjIFJldHVybnM6IFsxXSAwLjIyDQpzbmE6Omd0cmFucyh0ZXN0JG5ldHNbMiwsXSkgI3RyaWFkIGNlbnN1cyBhIGRpZmZlcmVudCB3YXkgDQogICMgUmV0dXJuczogWzFdIDAuMjg0MjEwNQ0KDQp0cmFuc2l0aXZpdHlfdzIgPC0gKDMgKiB0cmlhZF93MnJ1JFgzMDApLyh0cmlhZF93MnJ1JFgyMDEgKyAzICogdHJpYWRfdzJydSRYMzAwKSAjWDMwMCBpcyB2YXJpYWJsZSBmb3IgdHJhbnNpdGl2ZSB0cmlhZCAodGhlIGZ1bGx5IGNsb3NlZCB0cmlhZCkNCiMgd2UgbXVsdGlwbHkgYnkgMyBiZWNhdXNlIHRoZXJlIGFyZSAzIHBvc3NpYmxlIHRyYW5zaXRpdmUgdHJpYWRzDQp0cmFuc2l0aXZpdHlfdzINCiAgIyBSZXR1cm5zOiBbMV0gMC43NQ0KDQoNCg0KIyBXYXZlIDMNCnNuYTo6dHJpYWQuY2Vuc3VzKHRlc3QkbmV0c1szLCxdKQ0KICAgIyBSZXR1cm5zOiAwMDMgICAgMDEyICAgMTAyICAgMDIxRCAgMDIxVSAgMDIxQyAgMTExRCAgMTExVSAgMDMwVCAgMDMwQyAyMDEgIDEyMEQgIDEyMFUgMTIwQyAgMjEwICAzMDANCiAgICMgICAgWzEsXSAgNjU4Njc1IDEwNjU4IDQ2MiAgIDI4ICAgIDY4ICAgIDEzICAgICA4ICAgICAyICAgICA1ICAgIDAgICAgMCAgICAwICAgICAwICAgICAwICAgICAxICAgMA0KDQp1bmxvYWROYW1lc3BhY2UoInNuYSIpICAjSSB3aWxsIGRldGFjaCB0aGlzIHBhY2thZ2UgYWdhaW4NCg0KdHJpYWRfdzNydSA8LSBkYXRhLmZyYW1lKHNuYTo6dHJpYWQuY2Vuc3VzKHRlc3QkbmV0c1szLCxdKSkgI3NhdmUgYXMgZGYNCg0KDQppZ3JhcGg6OnRyYW5zaXRpdml0eSh0ZXN0X3dhdmUzcnUsIHR5cGUgPSAiZ2xvYmFsIikNCiAgIyBSZXR1cm5zOiBbMV0gMC4xMzEzODY5DQpzbmE6Omd0cmFucyh0ZXN0JG5ldHNbMywsXSkgI3RyaWFkIGNlbnN1cyBhIGRpZmZlcmVudCB3YXkgDQogICMgUmV0dXJuczogWzFdIDAuMjUNCg0KdHJhbnNpdGl2aXR5X3czIDwtICgzICogdHJpYWRfdzNydSRYMzAwKS8odHJpYWRfdzNydSRYMjAxICsgMyAqIHRyaWFkX3czcnUkWDMwMCkgI1gzMDAgaXMgdmFyaWFibGUgZm9yIHRyYW5zaXRpdmUgdHJpYWQgKHRoZSBmdWxseSBjbG9zZWQgdHJpYWQpDQojIHdlIG11bHRpcGx5IGJ5IDMgYmVjYXVzZSB0aGVyZSBhcmUgMyBwb3NzaWJsZSB0cmFuc2l0aXZlIHRyaWFkcw0KdHJhbnNpdGl2aXR5X3czDQogICMgUmV0dXJuczogWzFdIE5hTg0KDQpgYGANCg0KTkVFRCBUTyBJTkNMVURFIFRSSUFEUyAtIFRSQU5TSVRJVklUWSANCk9VVERFR1JFRSBBTkQgUkVDSVBST0NJVFkgQVJFIEFMV0FZUyBJTiBUSEVSRSwgQUxTTyBORUVEIE9VVERFR1JFRSBBQ1RJVklUWSBPUiBJTiBERUdSRUUgUE9QVUxBUklUWS4gQUxTTyBORUVEIFNPTUVUSElORyBGT1IgVFJBTlNJVElWSVRZIC0gR1dFU1AgVkFSSUFCTEUgQU5EIEVGRkVDVFMgVE8gSU5DTFVERSAtIE1BS0UgU1VSRSBUTyBJTkNMVURFIE9ORSBPRiBUSEVTRSBUT08uDQoNCg0KDQojIyBOZXR3b3JrIHZpc3VhbGlzYXRpb246IExldOKAmXMgbWFrZSBzaXplIHByb3BvcnRpb25hbCB0byBiZXR3ZWVubmVzcyBzY29yZQ0KYGBgIHtyfQ0KIyBjaGFuZ2luZyBWIG9mIFdhdmUxDQpWKHRlc3Rfd2F2ZTFydSkkc2l6ZSA9IGJldHdlZW5uZXNzKHRlc3Rfd2F2ZTFydSwgbm9ybWFsaXplZCA9IFQsIGRpcmVjdGVkID0gRkFMU0UpICogNjAgKyAxMCAgI2FmdGVyIHNvbWUgdHJpYWwgYW5kIGVycm9yDQoNCg0KICMjIG11bHRpcGxpY2F0aW9uIC0gY2hhbmdpbmcgNjAgY2hhbmdlcyB0aGUgZGlmZmVyZW5jZSBpbiBzaXplLCwgYWRkaW5nIDEwIG1ha2VzIHRoZSBzbWFsbGVzdCB2aXNpYmxlDQpwbG90KHRlc3Rfd2F2ZTFydSwgbW9kZSA9ICJ1bmRpcmVjdGVkIikNCiAjIyBzdHVjazogbmVlZCB0byByZW1vdmUgaWRzDQoNCg0KIyBpZ3JhcGgsIHdhbnQgbm8gb3ZlcmxhcDogaWdyYXBoIHBsb3R0aW5nIG5vIG92ZXJsYXAgLS0gYSBsb3Qgb2YgbGF5b3V0IGZ1bmN0aW9ucyAtLSB3YW50IHRvIGhvbGQgcHJpbnRpbmcgZGV2aWNlIGNvbnN0YW50LCBhbmQgdGhlbiByZWR1Y2Ugb3ZlcmxhcC4uLnRoZSBpZGVhIGlzIHRvIHB1c2ggbGVhc3QgY2VudHJhbCBlZ29zIG91dCANCg0Kc2V0LnNlZWQoMjM0NSkNCmwgPC0gbGF5b3V0X3dpdGhfbWRzKHRlc3Rfd2F2ZTFydSkgICNodHRwczovL2lncmFwaC5vcmcvci9kb2MvbGF5b3V0X3dpdGhfbWRzLmh0bWwNCnBsb3QodGVzdF93YXZlMXJ1LCBsYXlvdXQgPSBsKQ0KIyBzdG9yeSBpbiBzZWNvbmQgcGxvdDogNSBjbHVzdGVycyA/IChhcm91bmQgWFgsIFhYLCBYWCwgWFgsIGFuZCBYWCkgLSBhbmQgaW4tYmV0d2VlbiAod2hpY2ggd2Fzbid0IGFzIGNsZWFyIGJlZm9yZSkNCiAjIyBzdHVjazogbmVlZCB0byByZW1vdmUgaWRzDQoNCg0KI05PVEU6IFJFRiBMQUIgNCBUTyBNT0RJRlkgVEhFIFRIRSBTSVpJTkcvQVBQRUFSQU5DRSBPRiBUSEUgTkVUV09SSyBWSVNVQUxTDQoNCmBgYA0KDQoNCg0KDQoNCklGIE5FRURFRCBMQVRFUjoNCg0KIyBOb3csIHRyeSB0byBsb29wIGluIGdlbmRlciANCiMjIE1ha2Ugc2ltcGxlIGFkaiBtYXRyaXggZm9yIGdlbmRlciANCmBgYHtyfQ0KDQoNCmBgYA0KDQoNCg0K